﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Security;
using System.Xml.Linq;
using System.Net;
using System.IO;
using System.Threading;
using HigLabo.Net;

namespace HigLabo.WindowsAzure.RestService.ServiceManagement
{
    public class ManagementServiceClient
    {
        /// <summary>
        /// サブスクリプションID
        /// </summary>
        public String SubscriptionID { get; private set; }
        /// <summary>
        /// 証明書
        /// </summary>
        public X509Certificate2 Certificate { get; private set; }
        /// <summary>
        /// APIバージョン
        /// http://msdn.microsoft.com/en-us/library/windowsazure/gg592580.aspx
        /// </summary>
        public String APIVersion { get; set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="subscriptionID"></param>
        /// <param name="certThumbprint"></param>
        public ManagementServiceClient(String subscriptionID, String certThumbprint)
        {
            if (String.IsNullOrEmpty(certThumbprint))
            {
                throw new ArgumentException("certThumbprint");
            }
            Initialize(subscriptionID, GetCertificate(certThumbprint));
        }
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="subscriptionID"></param>
        /// <param name="certificate"></param>
        public ManagementServiceClient(String subscriptionID, X509Certificate2 certificate)
        {
            Initialize(subscriptionID, certificate);
        }
        /// <summary>
        /// 初期化メソッド
        /// </summary>
        /// <param name="subscriptionID"></param>
        /// <param name="certificate"></param>
        private void Initialize(String subscriptionID, X509Certificate2 certificate)
        {
            APIVersion = "2011-10-01";
            if (String.IsNullOrEmpty(subscriptionID))
            {
                throw new ArgumentException("subscriptionID");
            }
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }
            SubscriptionID = subscriptionID;
            Certificate = certificate;
        }
        /// <summary>
        /// 証明書の取得
        /// </summary>
        /// <param name="certThumbprint"></param>
        /// <returns></returns>
        public static X509Certificate2 GetCertificate(String certThumbprint)
        {
            var certCollection = GetCertificateCollection();
            var cert = certCollection.Find(X509FindType.FindByThumbprint, certThumbprint, false);
            // Check to see if our certificate was added to the collection. If no, throw an error, if yes, create a certificate using it.
            if (certCollection.Count == 0)
            {
                throw new ArgumentException("No certificate found containing thumbprint " + certThumbprint, "certThumbprint");
            }
            // Create an X509Certificate2 object using our matching certificate.
            return cert[0];
        }
        /// <summary>
        /// 証明書リストの取得
        /// </summary>
        /// <returns></returns>
        public static X509Certificate2Collection GetCertificateCollection()
        {
            // Create a reference to the My certificate store.
            X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            // Try to open the store.
            certStore.Open(OpenFlags.ReadOnly);
            // Find the certificate that matches the thumbprint.
            X509Certificate2Collection certCollection = certStore.Certificates;
            certStore.Close();
            return certCollection;
        }
        /// <summary>
        /// コマンドの実行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="command"></param>
        /// <returns></returns>
        public T Execute<T>(ManagementServiceCommand<T> command) where T : ManagementServiceResult, new()
        {
            // Build a URI for https://management.core.windows.net/<subscription-id>/<services|operations>/<operation-type>
            String requestUri = command.GetRequestUri();
            if (String.IsNullOrEmpty(requestUri))
                requestUri = String.Format("https://management.core.windows.net/{0}", this.SubscriptionID);
            else
                requestUri = String.Format("https://management.core.windows.net/{0}/{1}", this.SubscriptionID, requestUri);
            HttpClient cl = new HttpClient();
            cl.ClientCertificates.Add(this.Certificate);

            var cm = new HttpRequestCommand(requestUri, command.MethodName);
            if (string.IsNullOrEmpty(command.ContentType) == false)
            {
                cm.ContentType = command.ContentType;
            }
            cm.Headers["x-ms-version"] = this.APIVersion;
            var hd = command.GetRequestHeaders();
            if (hd != null)
            {
                foreach (var item in hd)
                {
                    cm.Headers[item.Key] = item.Value;
                }
            }
            var body = command.GetRequestBodyStream();
            HttpResponse response;
            if (body == null)
            {
                response = cl.GetResponse(cm);
            }
            else
            {
                cm.SetBodyStream(body);
                response = cl.GetResponse(cm);
            }
            var res = new T();
            res.SetResponseData(response);
            return res;
        }

        #region Command Execute Wrapping Method

        #region HostedServicesコマンド

        public UpdateDeploymentStatusResult UpdateDeploymentStatus(UpdateDeploymentStatusCommand command)
        {
            return Execute(command);
        }
        public CreateDeploymentResult CreateDeployment(CreateDeploymentCommand command)
        {
            return Execute(command);
        }
        public GetDeploymentResult GetDeployment(GetDeploymentCommand command)
        {
            return Execute(command);
        }
        public UpgradeDeploymentResult UpgradeDeployment(UpgradeDeploymentCommand command)
        {
            return Execute(command);
        }
        public GetHostedServicePropertiesResult GetHostedServiceProperties(GetHostedServicePropertiesCommand command)
        {
            return Execute(command);
        }
        public ListHostedServicesResult ListHostedServices(ListHostedServicesCommand command)
        {
            return Execute(command);
        }
        public SwapDeploymentResult SwapDeployment(SwapDeploymentCommand command)
        {
            return Execute(command);
        }

        public DeleteDeploymentResult DeleteDeployment(DeleteDeploymentCommand command)
        {
            return Execute(command);
        }

        public ChangeDeploymentConfigurationResult ChangeDeploymentConfiguration(ChangeDeploymentConfigurationCommand command)
        {
            return Execute(command);
        }

        public RebootRoleInstanceResult RebootRoleInstance(RebootRoleInstanceCommand command)
        {
            return Execute(command);
        }
        public ReimageRoleInstanceResult ReimageRoleInstance(ReimageRoleInstanceCommand command)
        {
            return Execute(command);
        }
        public RollbackUpdateOrUpgradeResult RollbackUpdateOrUpgrade(RollbackUpdateOrUpgradeCommand command)
        {
            return Execute(command);
        }
        public CreateHostedServiceResult CreateHostedService(CreateHostedServiceCommand command)
        {
            return Execute(command);
        }
        public UpdateHostedServiceResult UpdateHostedService(UpdateHostedServiceCommand command)
        {
            return Execute(command);
        }
        public DeleteHostedServiceResult DeleteHostedService(DeleteHostedServiceCommand command)
        {
            return Execute(command);
        }
        public WalkUpgradeDomainResult WalkUpgradeDomain(WalkUpgradeDomainCommand command)
        {
            return Execute(command);
        }
        #endregion

        #region StorageAccountsコマンド

        public ListStorageAccountsResult ListStorageAccounts(ListStorageAccountsCommand command)
        {
            return Execute(command);
        }
        public GetStorageAccountPropertiesResult GetStorageAccountProperties(GetStorageAccountPropertiesCommand command)
        {
            return Execute(command);
        }
        public GetStorageAccountKeysResult GetStorageAccountKeys(GetStorageAccountKeysCommand command)
        {
            return Execute(command);
        }
        public UpdateStorageAccountResult UpdateStorageAccount(UpdateStorageAccountCommand command)
        {
            return Execute(command);
        }
        public CreateStorageAccountResult CreateStorageAccount(CreateStorageAccountCommand command)
        {
            return Execute(command);
        }
        public DeleteStorageAccountResult DeleteStorageAccount(DeleteStorageAccountCommand command)
        {
            return Execute(command);
        }
        #endregion

        #region Certificatesコマンド
        public GetCertificateResult GetCertificate(GetCertificateCommand command)
        {
            return Execute(command);
        }
        public ListCertificatesResult ListCertificates(ListCertificatesCommand command)
        {
            return Execute(command);
        }
        public DeleteCertificateResult DeleteCertificate(DeleteCertificateCommand command)
        {
            return Execute(command);
        }
        public AddCertificateResult AddCertificate(AddCertificateCommand command)
        {
            return Execute(command);
        }
        #endregion

        #region Locationsコマンド
        public ListLocationsResult ListLocations(ListLocationsCommand command)
        {
            return Execute(command);
        }
        #endregion

        #region Affinity Groupsコマンド
        public ListAffinityGroupsResult ListAffinityGroups(ListAffinityGroupsCommand command)
        {
            return Execute(command);
        }
        public CreateAffinityGroupResult CreateAffinityGroup(CreateAffinityGroupCommand command)
        {
            return Execute(command);
        }
        public DeleteAffinityGroupResult DeleteAffinityGroup(DeleteAffinityGroupCommand command)
        {
            return Execute(command);
        }
        public UpdateAffinityGroupResult UpdateAffinityGroup(UpdateAffinityGroupCommand command)
        {
            return Execute(command);
        }
        public GetAffinityGroupPropertiesResult GetAffinityGroupProperties(GetAffinityGroupPropertiesCommand command)
        {
            return Execute(command);
        }
        #endregion

        #region Retrieving Subscription Historyコマンド
        public GetSubscriptionResult GetSubscription(GetSubscriptionCommand command)
        {
            return Execute(command);
        }
        public ListSubscriptionOperationsResult ListSubscriptionOperations(ListSubscriptionOperationsCommand command)
        {
            return Execute(command);
        }
        #endregion

        #region TrackingAsynchronousRequestsコマンド

        public GetOperationStatusResult GetOperationStatus(GetOperationStatusCommand command)
        {
            return Execute(command);
        }

        #endregion

        #region RetrievingOperatingSystemInformationコマンド
        public ListOperatingSystemFamiliesResult ListOperatingSystemFamilies(ListOperatingSystemFamiliesCommand command)
        {
            return Execute(command);
        }
        public ListOperatingSystemsResult ListOperatingSystems(ListOperatingSystemsCommand command)
        {
            return Execute(command);
        }
        #endregion

        #endregion
    }
}
